package com.opencee.cloud.api.gateway.filter;


import cn.hutool.core.collection.ConcurrentHashSet;
import com.opencee.cloud.api.gateway.config.ApiProperties;
import com.opencee.cloud.api.gateway.locator.ApiControlLocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.Set;

/**
 * 访问权限控制管理类
 *
 * @author liuyadu
 */
@Slf4j
@Component
public class AccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    private AntPathMatcher pathMatch = new AntPathMatcher();

    private Set<String> defaultPermitAll = new ConcurrentHashSet<>();

    private Set<String> defaultAuthorityIgnores = new ConcurrentHashSet<>();

    public AccessManager(ApiControlLocator apiControlLocator, ApiProperties apiProperties) {
        this.apiControlLocator = apiControlLocator;
        this.apiProperties = apiProperties;
        // 默认放行
        defaultPermitAll.addAll(Arrays.asList(new String[]{
                "/",
                "/favicon.ico",
                "/error",
                "/static/**",
                "/login/**"
        }));
    }

    private ApiControlLocator apiControlLocator;

    private ApiProperties apiProperties;


    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        ServerHttpRequest request = exchange.getRequest();
        String requestPath = exchange.getRequest().getURI().getPath();
        if (!apiProperties.getAccessControl()) {
            return Mono.just(new AuthorizationDecision(true));
        }
        // 是否直接放行
        if (permitAll(request)) {
            return Mono.just(new AuthorizationDecision(true));
        }
        return authentication.map(auth -> {
            return new AuthorizationDecision(checkAuthorities(request, auth));
        }).defaultIfEmpty(new AuthorizationDecision(false));
    }

    /**
     * 始终放行
     *
     * @param request
     * @return
     */
    public boolean permitAll(ServerHttpRequest request) {
        boolean permit = defaultPermitAll.stream().filter(r -> pathMatch.match(r, request.getPath().value())).findFirst().isPresent();
        if (permit) {
            return true;
        }
        if (apiProperties != null && apiProperties.getPermitAll() != null && !apiProperties.getPermitAll().isEmpty()) {
            permit = apiProperties.getPermitAll().stream().filter(r -> pathMatch.match(r, request.getPath().value())).findFirst().isPresent();
        }
        return permit;
    }


    /**
     * 忽略鉴权
     */
    private boolean authorityIgnores(String requestPath) {
        boolean permit = defaultAuthorityIgnores.stream().filter(r -> pathMatch.match(r, requestPath)).findFirst().isPresent();
        if (permit) {
            return true;
        }
        if (apiProperties != null && apiProperties.getAuthorityIgnores() != null && !apiProperties.getAuthorityIgnores().isEmpty()) {
            permit = apiProperties.getAuthorityIgnores().stream().filter(r -> pathMatch.match(r, requestPath)).findFirst().isPresent();
        }
        return permit;
    }

    /**
     * 检查权限
     *
     * @param request
     * @param authentication
     * @return
     */
    private boolean checkAuthorities(ServerHttpRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        // 已认证身份
        if (principal != null) {
            if (authentication instanceof AnonymousAuthenticationToken) {
                //check if this uri can be access by anonymous
                return false;
            }
            if (authorityIgnores(request.getPath().value())) {
                // 认证通过,并且无需权限
                return true;
            }
            return true;
        }
        return false;
    }

    /*
     *查询资源状态
     *
     *@return
     */
//    public ResourceInfo getAuthorityInfo(ServerHttpRequest request) {
//        AtomicReference<ResourceInfo> api = new AtomicReference<>();
//        apiControlLocator.getGrantedAuthorities().keySet().stream()
//                .filter(m -> m.matches(request))
//                .filter(m -> ResourceType.api.name().toUpperCase().equals(apiControlLocator.getGrantedAuthorities().get(m).getResourceType()))
//                .findFirst().ifPresent(m -> {
//            api.set(apiControlLocator.getGrantedAuthorities().get(m));
//        });
//        // 动态权限列表
//        return api.get();
//    }


    /*  public boolean mathAuthorities(ServerHttpRequest request, Authentication authentication) {
          Collection<ConfigAttribute> attributes = getAttributes(request);
          int result = 0;
          int expires = 0;
          if (authentication == null) {
              return false;
          } else {
              if (BaseConstants.ROOT.equals(authentication.getName())) {
                  // 默认超级管理员账号,直接放行
                  return true;
              }
              Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
              Iterator var6 = attributes.iterator();
              while (var6.hasNext()) {
                  ConfigAttribute attribute = (ConfigAttribute) var6.next();
                  Iterator var8 = authorities.iterator();
                  while (var8.hasNext()) {
                      GrantedAuthority authority = (GrantedAuthority) var8.next();
                      if (attribute.getAttribute().equals(authority.getAuthority())) {
                          result++;
                          if (authority instanceof GrantedDateAuthority) {
                              GrantedDateAuthority customer = (GrantedDateAuthority) authority;
                              if (customer.getIsExpired() != null && customer.getIsExpired()) {
                                  // 授权过期数
                                  expires++;
                              }
                          }
                      }
                  }
              }
              log.debug("mathAuthorities result[{}] expires[{}]", result, expires);
              if (expires > 0) {
                  // 授权已过期
                  throw new AccessDeniedException(ErrorCode.ACCESS_DENIED_AUTHORITY_EXPIRED.getMessage());
              }
              return result > 0;
          }
      }

      private Collection<ConfigAttribute> getAttributes(ServerHttpRequest request) {
          // 匹配动态权限
          AtomicReference<Collection<ConfigAttribute>> attributes = new AtomicReference<>();
          apiControlLocator.getConfigAttributes().keySet().stream()
                  .filter(m -> m.matches(request))
                  .findFirst().ifPresent(m -> {
              attributes.set(apiControlLocator.getConfigAttributes().get(m));
          });
          if (attributes.get() != null) {
              return attributes.get();
          }
          return SecurityConfig.createList("AUTHORITIES_REQUIRED");
      }

      *//*
     *IP黑名单验证
     *
     *
     *@return
     *//*
    public boolean matchIpOrOriginBlacklist(ServerHttpRequest request) {
        return apiControlLocator.getIpBlacks().keySet().stream()
                .filter(m -> m.matches(request))
                .filter(m -> matchIpOrOrigin(apiControlLocator.getIpBlacks().get(m).getIpAddressSet(), request))
                .findFirst().isPresent();
    }

    *//*
     *白名单验证
     *
     *
     *@return[hasWhiteList,allow]
     *//*

    public Boolean[] matchIpOrOriginWhiteList(ServerHttpRequest request) {
        final Boolean[] result = {false, false};
        apiControlLocator.getIpWhites().keySet().stream()
                .filter(m -> m.matches(request))
                .findFirst().ifPresent(m -> {
            result[0] = true;
            result[1] = matchIpOrOrigin(apiControlLocator.getIpWhites().get(m).getIpAddressSet(), request);
        });
        return result;
    }


    *//*
     *匹配IP或域名
     *
     *
     @param
     values
     *
     @param
     request
     *@return
     *//*

    public boolean matchIpOrOrigin(Set<String> values, ServerHttpRequest request) {
        if (values == null || values.isEmpty()) {
            return false;
        }
        String remoteIpAddress = ReactiveWebUtils.getRemoteAddress(request);
        String origin = request.getHeaders().getOrigin();
        ReactiveIpAddressMatcher ipAddressMatcher = null;
        for (String value : values) {
            if (StringHelper.matchIp(value)) {
                ipAddressMatcher = new ReactiveIpAddressMatcher(value);
                if (ipAddressMatcher.matches(remoteIpAddress)) {
                    return true;
                }
            } else {
                if (StringHelper.matchDomain(value) && StringUtils.isNotBlank(origin) && origin.contains(value)) {
                    return true;
                }
            }
        }
        return false;
    }
*/
    public ApiProperties getApiProperties() {
        return apiProperties;
    }

    public void setApiProperties(ApiProperties apiProperties) {
        this.apiProperties = apiProperties;
    }
}
